package com.lex.crossgate.admin.common.support.shiro;


import com.lex.crossgate.admin.common.support.jwt.JWTToken;
import com.lex.crossgate.admin.common.support.jwt.JWTUtil;
import com.lex.crossgate.admin.common.utils.ShiroUtils;
import com.lex.crossgate.common.utils.SpringContextUtils;
import com.lex.crossgate.admin.modules.sys.entity.SysUser;
import com.lex.crossgate.admin.modules.sys.service.SysMenuService;
import com.lex.crossgate.admin.modules.sys.service.SysUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Set;


/**
 *
 */
public class MyShiroRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService sysUserService;


    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 大坑！，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 认证信息.(身份验证)
     * Authentication 是用来验证用户身份
     * @param auth
     * @return
     * @throws AuthenticationException
    */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        String userName = JWTUtil.getUsername(token);
        if (userName == null) {
            throw new AuthenticationException("token invalid");
        }

        SysUser sysUser = sysUserService.getByUserName(userName);
        if (sysUser == null) {
            throw new AuthenticationException("User didn't existed!");
        }

        if (! JWTUtil.verify(token, userName, sysUser.getPassWord())) {
            throw new AuthenticationException("Username or password error");
        }

        return new SimpleAuthenticationInfo(sysUser, token, "myShiroRealm");

    }

    /**
     * 此方法调用  hasRole,hasPermission的时候才会进行回调.
     *
     * 权限信息.(授权):
     * 1、如果用户正常退出，缓存自动清空；
     * 2、如果用户非正常退出，缓存自动清空；
     * 3、如果我们修改了用户的权限，而用户不退出系统，修改的权限无法立即生效。
     * （需要手动编程进行实现；放在service进行调用）
     * 在权限修改后调用realm中的方法，realm已经由spring管理，所以从spring中获取realm实例，
     * 调用clearCached方法；
     * :Authorization 是授权访问控制，用于对用户进行的操作授权，证明该用户是否允许进行当前操作，如访问某个链接，某个资源文件等。
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //从redis缓存中查询权限
        Object next = principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = null;
        if(next instanceof SysUser) { // 避免授权报错
            String userId = ShiroUtils.getUserId();
            SysMenuService menuService = SpringContextUtils.getBean(SysMenuService.class);
            Set<String> permsSet = menuService.listPerms(userId);
            info = new SimpleAuthorizationInfo();
            info.setStringPermissions(permsSet);
        }
        return info;
    }

}
